/*
* Copyright (c) Huawei Technologies Co., Ltd. 2020-2021. All rights reserved.
* gazelle is licensed under the Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*     http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
* PURPOSE.
* See the Mulan PSL v2 for more details.
*/
#include <rte_ethdev.h>

#include "lwipgz_flow.h"

#include "lstack_log.h"
#include "lstack_rule.h"
#include "lstack_pkt_dispatch.h"

#define IS_ARP_PKT(ptype) ((ptype & RTE_PTYPE_L2_ETHER_ARP) == RTE_PTYPE_L2_ETHER_ARP)
#define IS_IPV4_TCP_PKT(ptype) (RTE_ETH_IS_IPV4_HDR(ptype) && \
        ((ptype & RTE_PTYPE_L4_TCP) == RTE_PTYPE_L4_TCP) && \
        ((ptype & RTE_PTYPE_L4_FRAG) != RTE_PTYPE_L4_FRAG) && \
        (RTE_ETH_IS_TUNNEL_PKT(ptype) == 0))

#define IS_IPV6_TCP_PKT(ptype) (RTE_ETH_IS_IPV6_HDR(ptype) && \
        ((ptype & RTE_PTYPE_L4_TCP) == RTE_PTYPE_L4_TCP) && \
        ((ptype & RTE_PTYPE_L4_FRAG) != RTE_PTYPE_L4_FRAG) && \
        (RTE_ETH_IS_TUNNEL_PKT(ptype) == 0))

#define IS_IPV4_UDP_PKT(ptype) (RTE_ETH_IS_IPV4_HDR(ptype) && \
        ((ptype & RTE_PTYPE_L4_UDP) == RTE_PTYPE_L4_UDP) && \
        (RTE_ETH_IS_TUNNEL_PKT(ptype) == 0))

#define IS_IPV6_UDP_PKT(ptype) (RTE_ETH_IS_IPV6_HDR(ptype) && \
        ((ptype & RTE_PTYPE_L4_UDP) == RTE_PTYPE_L4_UDP) && \
        (RTE_ETH_IS_TUNNEL_PKT(ptype) == 0))

#define IS_ICMPV6_PKT(ptype) (RTE_ETH_IS_IPV6_HDR(ptype) && \
        ((ptype & RTE_PTYPE_L4_ICMP) == RTE_PTYPE_L4_ICMP) && \
        (RTE_ETH_IS_TUNNEL_PKT(ptype) == 0))

#define CONSTRUCT_V4_QTUPLE(qtuple, l3_hdr, l4_hdr) \
    do { \
        qtuple.protocol = GZ_ADDR_TYPE_V4; \
        qtuple.src_ip.u_addr.ip4.addr = l3_hdr->dst_addr; \
        qtuple.dst_ip.u_addr.ip4.addr = l3_hdr->src_addr; \
        qtuple.src_port = l4_hdr->dst_port; \
        qtuple.dst_port = l4_hdr->src_port; \
    } while (0)


#define CONSTRUCT_V4_TCP_QTUPLE(qtuple, l3_hdr, l4_hdr) \
    do { \
        CONSTRUCT_V4_QTUPLE(qtuple, l3_hdr, l4_hdr); \
        qtuple.protocol = GZ_ADDR_TYPE_TCP | GZ_ADDR_TYPE_V4; \
    } while (0)

#define CONSTRUCT_V4_UDP_QTUPLE(qtuple, l3_hdr, l4_hdr) \
    do { \
        CONSTRUCT_V4_QTUPLE(qtuple, l3_hdr, l4_hdr); \
        qtuple.protocol = GZ_ADDR_TYPE_UDP | GZ_ADDR_TYPE_V4; \
    } while (0)


static int tcp_v4_syn_handle(struct rte_ipv4_hdr *ipv4_hdr, struct rte_tcp_hdr *tcp_hdr, uint16_t cur_queue)
{
    struct gazelle_quintuple qtuple = {0};
    CONSTRUCT_V4_TCP_QTUPLE(qtuple, ipv4_hdr, tcp_hdr);
    return vdev_reg_xmit(REG_RING_FIND_LISTEN_PORT, &qtuple);
}

static int tcp_v4_handle(struct rte_mbuf *mbuf, int cur_queue)
{
    struct gazelle_quintuple qtuple = {0};
    struct rte_ether_hdr *eth_hdr;
    struct rte_ipv4_hdr *ipv4_hdr;
    struct rte_tcp_hdr *tcp_hdr;

    eth_hdr = rte_pktmbuf_mtod(mbuf, struct rte_ether_hdr *);
    ipv4_hdr = (struct rte_ipv4_hdr *)((char *)eth_hdr + mbuf->l2_len);
    tcp_hdr = (struct rte_tcp_hdr *)((char *)ipv4_hdr + mbuf->l3_len);

    if (((tcp_hdr->tcp_flags & RTE_TCP_ACK_FLAG) != RTE_TCP_ACK_FLAG) &&
        ((tcp_hdr->tcp_flags & RTE_TCP_SYN_FLAG) == RTE_TCP_SYN_FLAG)) {
        return tcp_v4_syn_handle(ipv4_hdr, tcp_hdr, cur_queue);
    }

    CONSTRUCT_V4_TCP_QTUPLE(qtuple, ipv4_hdr, tcp_hdr);
    return vdev_reg_xmit(REG_RING_FIND_CONN, &qtuple);
}

#if 0
static enum dispatch_type udp_handle(struct rte_mbuf *mbuf)
{
    uint16_t queue_id;
    struct rte_ether_hdr *eth_hdr;
    struct rte_ipv4_hdr *ipv4_hdr;
    struct rte_udp_hdr *udp_hdr;

    eth_hdr = rte_pktmbuf_mtod(mbuf, struct rte_ether_hdr *);
    ipv4_hdr = (struct rte_ipv4_hdr *)((char *)eth_hdr + mbuf->l2_len);
    udp_hdr = (struct rte_udp_hdr *)((char *)ipv4_hdr + mbuf->l3_len);

    CONSTRUCT_UDP_TUPLE(qtuple, ipv4_hdr, udp_hdr);
}
#endif

enum dispatch_type dispatch_packet(struct rte_mbuf *mbuf, int cur_queue, int *dst_queue)
{
    if (unlikely(IS_ARP_PKT(mbuf->packet_type) || IS_ICMPV6_PKT(mbuf->packet_type))) {
        return DISPATCH_TO_ALL;
    } else if (IS_IPV4_TCP_PKT(mbuf->packet_type)) {
        *dst_queue = tcp_v4_handle(mbuf, cur_queue);
        if (*dst_queue < 0) {
            return DISPATCH_TO_KERNEL;
        } else if (*dst_queue == cur_queue) {
            return DISPATCH_TO_CUR_THREAD;
        } else {
            return DISPATCH_TO_OTHER_THREAD;
        }
    } else if (IS_IPV6_TCP_PKT(mbuf->packet_type)) {
        return DISPATCH_TO_CUR_THREAD;
    } else if (IS_IPV4_UDP_PKT(mbuf->packet_type)) {
        return DISPATCH_TO_CUR_THREAD;
    } else if (IS_IPV6_UDP_PKT(mbuf->packet_type)) {
        return DISPATCH_TO_CUR_THREAD;
    } else {
        //return DISPATCH_TO_KERNEL;
        return DISPATCH_TO_CUR_THREAD;
    }
}
